#ifndef TCPMESSAGE_H
#define TCPMESSAGE_H
#include <cstdint>
#include <vector>
#include <QByteArray>
#include <QObject>
#include <QDataStream>
#include <QSet>
#include <QDebug>

enum class MessageType
{
    HeartbeatRequest = 0x01,
    LoginRequest = 0x02,
    SetRegRequest = 0x40,
    GetRegRequest = 0x41,
    RTKRequest = 0x00010005,
    RTKData = 0x00010006,
    TrackDataV2 = 0x00020002,
    TrackDataV3 = 0x00030002,
};

class LoginRequestMessage
{
public:
    // 用于发送请求
    uint32_t role;
    uint32_t code;
    // 用于接收响应
    uint16_t status = 0x0000;

    void parseBody(QByteArray& data)
    {
        QDataStream stream(&data, QIODevice::ReadOnly);
        stream.setByteOrder(QDataStream::BigEndian);
        stream >> role;
        stream >> status;
    }
    QByteArray dumpBody()
    {
        QByteArray data;
        QDataStream stream(&data, QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::BigEndian);
        stream << role;
        stream << code;

        return data;
    }
};

class HeartbeatMessage
{
public:
    int64_t timestamp;

    void parseBody(QByteArray& data)
    {
        QDataStream stream(&data, QIODevice::ReadOnly);
        stream.setByteOrder(QDataStream::BigEndian);
        stream >> timestamp;
    }
    QByteArray dumpBody()
    {
        QByteArray data;
        QDataStream stream(&data, QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::BigEndian);
        stream << timestamp;

        return data;
    }
};

typedef QPair<uint32_t, uint32_t> Register;
typedef QList<Register> RegisterList;

class SetRegRequestMessage
{
public:
    RegisterList registers;

    bool parseBody(QByteArray& data)
    {
        QDataStream stream(&data, QIODevice::ReadOnly);
        stream.setByteOrder(QDataStream::BigEndian);
        int32_t regNum = 0;
        registers.clear();
        if (static_cast<size_t>(data.size()) < sizeof(uint32_t))
        {
            return false;
        }
        stream >> regNum;
        if (static_cast<size_t>(data.size()) != (regNum * 2 * sizeof(uint32_t) + sizeof(uint32_t)))
        {
            return false;
        }
        for (int i=0; i<regNum; ++i)
        {
            uint32_t addr, value;
            stream >> addr;
            stream >> value;
            registers.push_back(QPair<uint32_t, uint32_t>(addr, value));
        }
        return true;
    }

    QByteArray dumpBody()
    {
        QByteArray data;
        QDataStream stream(&data, QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::BigEndian);

        int32_t regNum = registers.size();
        stream << regNum;
        for (int i=0; i<regNum; ++i)
        {
            stream << registers.at(i).first;
            stream << registers.at(i).second;
        }

        return data;
    }
};

typedef SetRegRequestMessage GetRegResponseMessage;

class GetRegRequestMessage
{
public:
    QList<uint32_t> registerIds;

    QByteArray dumpBody()
    {
        QByteArray data;
        QDataStream stream(&data, QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::BigEndian);

        int32_t regNum = registerIds.size();
        stream << regNum;
        for (int i=0; i<regNum; ++i)
        {
            stream << registerIds[i];
        }

        return data;
    }
};

class RTKRequestMessage
{
public:
    // 用于发送请求
    int8_t command;
    int8_t reserve;
    // 用于接收响应
    int16_t status;

public:
    void parseBody(QByteArray& data)
    {
        QDataStream stream(&data, QIODevice::ReadOnly);
        stream.setByteOrder(QDataStream::BigEndian);
        stream >> status;
    }
    QByteArray dumpBody()
    {
        QByteArray data;
        QDataStream stream(&data, QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::BigEndian);
        stream << command;
        stream << reserve;

        return data;
    }
};

class RTKDataMessage
{
public:
    int64_t lat; // 纬度：分辨率：1e-9°
    int64_t lon; // 经度：分辨率：1e-9°
    int64_t azimuth; //雷达指向（雷达转台0度与正北的偏移）：分辨率：1e-9°
    int32_t numberOfsatellite; // 卫星数量
    int32_t height; //无效

public:
    bool parseBody(QByteArray& array)
    {
        QDataStream stream(&array, QIODevice::ReadOnly);
        stream.setByteOrder(QDataStream::BigEndian);
        stream >> lat;
        stream >> lon;
        stream >> azimuth;   ////方位
        stream >> numberOfsatellite;
        stream >> height;

        return true;
    }
};


class TrackDataV2Message
{
public:
    struct Point {
        int32_t distance;  ///< 距离,分辨率：0.01米
        int32_t angle; ///< 方位角度，分辨率：0.0001度
        int32_t speed; ///< 速度，分辨率：0.01米/秒
        int32_t speedDirection; ///< 速度方向，分辨率：0.0001度
        int16_t snr; ///< 信噪比，分辨率：0.01dB
        uint16_t id;    //id
        int8_t object;
        int8_t pluse;
        int16_t beam;
        int16_t doppler;
        int16_t dopplerWidth;
        // 预测值
        int32_t privDistance; ///< 预测距离,分辨率：0.01米
        int32_t privAngle; ///< 预测角度，分辨率：0.0001度
        int32_t privSpeed; ///< 预测速度，分辨率：0.01米/秒
        int32_t privSpeedDirection; ///< 预测速度方向，分辨率：0.0001度
    };
public:
    int64_t timestamp;
    uint32_t frame;
    std::vector<Point> points;

public:
    bool parseBody(QByteArray array)
    {
        int32_t pointNum = 0;
        QDataStream stream(&array, QIODevice::ReadOnly);
        stream.setByteOrder(QDataStream::BigEndian);
        stream >> timestamp;
        stream >> frame;
        stream >> pointNum;
        points.clear();
        for (int i=0; i<pointNum; i++)
        {
            Point p;
            stream >> p.distance;
            stream >> p.angle;
            stream >> p.speed;
            stream >> p.speedDirection;
            stream >> p.snr;
            stream >> p.id;
            stream >> p.object;
            stream >> p.pluse;
            stream >> p.beam;
            stream >> p.doppler;
            stream >> p.dopplerWidth;
            stream >> p.privDistance;
            stream >> p.privAngle;
            stream >> p.privSpeed;
            stream >> p.privSpeedDirection;
            points.push_back(p);
        }
        return true;
    }
};

class TrackDataV3Message
{
public:
    struct Point {
        int32_t x; 		///< x轴坐标, 分辨率：0.01米
        int32_t y; 		///< y轴坐标, 分辨率：0.01米
        int32_t z; 		///< z轴坐标, 分辨率：0.01米
        int32_t vx; 	///< x轴速度, 分辨率：0.01米/秒
        int32_t vy; 	///< y轴速度, 分辨率：0.01米/秒
        int32_t vz; 	///< z轴速度, 分辨率：0.01米/秒
        uint32_t id;	///< 航迹ID，用于区分不同目标
        int16_t snr; 	///< 信噪比，分辨率：0.01dB，调试版本上位机在航迹标签中显示该信息
        int16_t rcs;    ///< 雷达散射截面积，分辨率0.01m2（标签显示）
        int16_t beam; 	///< 波位索引，调试版本上位机在航迹标签中显示该信息
        uint16_t di; 	///< 多普勒单元索引，调试版本上位机在航迹标签中显示该信息
        uint16_t ri; 	///< 距离单元索引，调试版本上位机在航迹标签中显示该信息
        int8_t object;  ///< 目标类型
        int8_t pluse;
        // 预测值
        int64_t timestamp; ///<航迹更新时间戳，单位：ms，用于计算预测值
        int32_t dump0; ///< 预留 不需要解析
        int32_t dump1; ///< 预留 不需要解析
        int32_t dump2; ///< 预留 不需要解析
        int32_t selected; ///< 选中标志
    };
public:
    int64_t timestamp;
    int64_t frame;
    int64_t frameTimestamp; ///< 当前帧时间戳，单位ms，用于计算预测值
    int32_t scanBoundaryA; ///< 扫描左边界，分辨率：0.0001度
    int32_t scanBoundaryB; ///< 扫描右边界，分辨率：0.0001度
    int32_t scanningDirection; ///< 扫描方向，0: 顺时针扫描, 2: 逆时针扫描
    std::vector<Point> points;

public:
    bool parseBody(QByteArray array)
    {
        int32_t pointNum = 0;
        QDataStream stream(&array, QIODevice::ReadOnly);
        stream.setByteOrder(QDataStream::BigEndian);
        stream >> timestamp;
        stream >> frame;
        stream >> frameTimestamp;
        stream >> scanBoundaryA;
        stream >> scanBoundaryB;
        stream >> scanningDirection;
        stream >> pointNum;
        points.clear();
        for (int i=0; i<pointNum; i++)
        {
            Point p;
            stream >> p.x;
            stream >> p.y;
            stream >> p.z;
            stream >> p.vx;
            stream >> p.vy;
            stream >> p.vz;
            stream >> p.id;
            stream >> p.snr;
            stream >> p.rcs;
            stream >> p.beam;
            stream >> p.di;
            stream >> p.ri;
            stream >> p.object;
            stream >> p.pluse;
            stream >> p.timestamp;
            stream >> p.dump0;
            stream >> p.dump1;
            stream >> p.dump2;
            stream >> p.selected;

            points.push_back(p);
        }
        return true;
    }
};

class RadarProtocol
{
public:
    static void makePacket(MessageType type, const QByteArray &data, QByteArray &packet);
    static MessageType parsePacket(QByteArray &packet, QByteArray &data);

    static uint16_t CRC16 (const uint8_t *updata, int len)
    {
        uint8_t uchCRCHi = 0xff;
        uint8_t uchCRCLo = 0xff;
        uint16_t  uindex;

        while(len--)
        {
            uindex = uchCRCHi ^ *updata++;
            uchCRCHi = uchCRCLo ^ auchCRCHi[uindex];
            uchCRCLo = auchCRCLo[uindex];
        }
        return ((uchCRCHi<<8)|uchCRCLo);
    }

public:
    static uint32_t requestId;
    static uint32_t responseId;
    static uint8_t auchCRCHi[];
    static uint8_t auchCRCLo[];
};



#endif // TCPMESSAGE_H
